package com.leyou.auth.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.leyou.auth.config.JwtProperties;
import com.leyou.auth.mapper.ApplicationInfoMapper;
import com.leyou.auth.pojo.ApplicationInfo;
import com.leyou.common.auth.pojo.AppInfo;
import com.leyou.common.auth.pojo.Payload;
import com.leyou.common.auth.pojo.UserInfo;
import com.leyou.common.auth.utils.JwtUtils;
import com.leyou.common.exception.pojo.ExceptionEnum;
import com.leyou.common.exception.pojo.LyException;
import com.leyou.common.utils.CookieUtils;
import com.leyou.user.client.UserClient;
import com.leyou.user.pojo.User;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class AuthService {

    @Autowired
    private UserClient userClient;

    @Autowired
    private JwtProperties jwtProps;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ApplicationInfoMapper applicationInfoMapper;
    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    public void login(String username, String password, HttpServletRequest request, HttpServletResponse response) {
        //1.验证用户名和密码是否正确
        User loginUser = userClient.findUserByUserNameAndPwd(username,password);

        UserInfo userInfo = new UserInfo(loginUser.getId(), loginUser.getUsername(), "admin");

        //2.生成token和写出Cookie
        buildTokenAndWriteCookie(response,userInfo);
    }

    /**
     * 生成token和写出Cookie
     * @param response
     * @param userInfo
     */
    private void buildTokenAndWriteCookie(HttpServletResponse response, UserInfo userInfo) {
        //2.生成Token
        String token = JwtUtils.generateTokenExpireInMinutes(
                userInfo,
                jwtProps.getPrivateKey(),
                jwtProps.getCookie().getExpire()
        );
        //3.写出Cookie给浏览器
        CookieUtils.newCookieBuilder()
                .name(jwtProps.getCookie().getCookieName())
                .value(token)
                .domain(jwtProps.getCookie().getCookieDomain())
                .response(response)
                .build();
    }

    public UserInfo verify(HttpServletRequest request, HttpServletResponse response) {
        //1.从request取出Cookie(token)
        String token = CookieUtils.getCookieValue(request, jwtProps.getCookie().getCookieName());

        //2.校验token合法性
        Payload<UserInfo> payload = null;

        try {
            payload = JwtUtils.getInfoFromToken(token,jwtProps.getPublicKey(),UserInfo.class);
        } catch (Exception e) {
            e.printStackTrace();
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }

        // 判断当前token是否在黑名单中，如果在，拒绝访问
        if(redisTemplate.hasKey(payload.getId())){
            throw new LyException(ExceptionEnum.UNAUTHORIZED);
        }

        // 计算是否需要刷新token

        // 1.获取token的过期时间
        Date expireTime = payload.getExpiration();

        // 2.计算token的刷新时间（过期时间 - 15）
        DateTime refreshTime = new DateTime(expireTime).minusMinutes(jwtProps.getCookie().getRefreshTime());

        // 3.判断刷新时间<当前时间,重新生成token
        if(refreshTime.isBeforeNow()){

            //把刷新前的token也放入黑名单
            addTokenToBlackList(token);
            //重新生成token
            buildTokenAndWriteCookie(response,payload.getInfo());
        }

        //3.取出荷载数据（登录用户数据）
        return payload.getInfo();
    }

    public void logout(HttpServletRequest request, HttpServletResponse response) {
        //取出token
        String token = CookieUtils.getCookieValue(request, jwtProps.getCookie().getCookieName());
        System.out.println(jwtProps.getCookie().getCookieName()); //================

        // 1.删除Cookie
        CookieUtils.deleteCookie(
                jwtProps.getCookie().getCookieName(), // cookie名称
                jwtProps.getCookie().getCookieDomain(), // cookie的域
                response
        );

        //2.把token加入黑名单列表(redis)
        addTokenToBlackList(token);
    }

    /**
     * 把token加入黑名单
     * @param token
     */
    private void addTokenToBlackList(String token) {
        // 取出token的ID
        Payload<UserInfo> payload = JwtUtils.getInfoFromToken(token, jwtProps.getPublicKey(), UserInfo.class);
        String tokenId = payload.getId();

        //计算剩余时间
        Date expireTime = payload.getExpiration(); // 过期时间
        long remainTime = expireTime.getTime() - System.currentTimeMillis();

        redisTemplate.opsForValue().set(tokenId,"1",remainTime, TimeUnit.MILLISECONDS);
    }

    /**
     * 验证服务名称和服务密码是否合法
     */
    public ApplicationInfo findAppByServiceNameAndSecret(String serviceName, String secret){
        //1.判断服务名称是否存在
        ApplicationInfo info = new ApplicationInfo();
        info.setServiceName(serviceName);
        QueryWrapper<ApplicationInfo> queryWrapper = Wrappers.query(info);
        ApplicationInfo loginAppInfo = applicationInfoMapper.selectOne(queryWrapper);

        if(loginAppInfo==null){
            throw new LyException(ExceptionEnum.INVALID_SERVER_ID_SECRET);
        }

        //2.判断服务密钥是否正确（参数一:传过来的密码与查到的密码比对）
        if(!passwordEncoder.matches(secret,loginAppInfo.getSecret())){
            throw new LyException(ExceptionEnum.INVALID_SERVER_ID_SECRET);
        }
        return loginAppInfo;
    }

    public String authorization(String serviceName, String secret) {
        // 1.校验服务合法性
        ApplicationInfo loginAppInfo = findAppByServiceNameAndSecret(serviceName, secret);

        // 2.创建AppInfo对象
        // 获取当前服务的目标服务列表
        List<String> targetList = applicationInfoMapper.findTargetListByServiceName(serviceName);
        AppInfo appInfo = new AppInfo(loginAppInfo.getId(), loginAppInfo.getSecret(), targetList);

        // 3.使用JwtUtils产生服务token
        String token = JwtUtils.generateTokenExpireInMinutes(appInfo,jwtProps.getPrivateKey(),jwtProps.getApp().getExpire());

        // 4.返回服务token
        return token;
    }
}
